/**
 *
 */
package com.elcom.emocbc.cbm;

import com.elcom.emocbc.broadcastscheduler.dao.ConnectionProvider;
import com.elcom.emocbc.broadcastscheduler.dao.ConnectionProviderManager;
import com.elcom.emocbc.broadcastscheduler.zonetimelinebuilder.ZoneTimeslotBuilder;
import com.elcom.emocbc.cbm.randomprogram.ScheduleRebuild;
import com.elcom.emocbc.cbm.randomprogram.ScheduleRebuildCommandChecker;
import com.elcom.emocbc.cbm.smsbroadcast.SmsBroadcastConfig;
import com.elcom.emocbc.cbm.smsbroadcast.SmsBroadcastMain;
import com.elcom.emocbc.cbm.smsbroadcast.SmsBroadcaster;
import com.elcom.emocbc.common.ipc.cbcgwmessage.CBM_SUBMIT_REQ;
import com.elcom.emocbc.common.ipc.cbcgwmessage.CBM_SUBMIT_RESP;
import com.elcom.emocbc.db.CellBroadcastDAO;
import com.elcom.emocbc.dbmapping.persistence.DbAccess;
import com.elcom.emocbc.ipc.IPCCellBroadcastClientTrap;
import com.elcom.emocbc.util.BaseThreadFactory;
import com.elcom.emocbc.util.ConfigurationManager;
import com.elcom.emocbc.util.GeneratorSeqIPC;
import com.elcom.emocbc.util.Hex;
import com.elcom.ipc.synchronize.transport.pending_response.InvalidResponseException;
import com.elcom.ipc.synchronize.transport.pending_response.ResponseTimeoutException;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Properties;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.PropertyConfigurator;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author tiamo114
 *
 */
public class CellBroadcastService {

    private static final Logger logger = LoggerFactory.getLogger(CellBroadcastService.class);
    public static final int NORMAL = 0;
    public static final int PROCESSING = 5;
    public static final int SIZE_TO_COPY = 79;
    private static final String ACTION_CODE = "30";
    private MonitorThread monitorThread = null;
    private BroadcastThread broadcastThread = null;
    private final BroadcastUnit[] broadcastUnits;
    private SynchronousQueue<BroadcastDTO> q = null;
    private Scheduler sched = null;
    private static IPCCellBroadcastClientTrap ipcClient = null;
    //Config Parameters
    public static int numberBroadCastUnit;
    public static int maxTime;
    public static int minTime;
    public static long timeout;
    public static long interval;
    public static int cycleGetBroadcast;

    public CellBroadcastService(String ipcName) throws Throwable {
        PropertyConfigurator.configureAndWatch(System.getProperty("user.home") + File.separator + "config/cellbroadcast" + File.separator + "log4j.properties");
        reLoadConfig();

        setupDatabaseAccess();

        Thread.sleep(15000L);

        scheduleRebuildAllZoneTimeline();

        scheduleTimeslotBuilder();

        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        sched = sf.getScheduler();
        logger.info("------- Initialization Complete -----------");

        sched.start();
        logger.info("------- Started Scheduler -----------------");

        ipcClient = new IPCCellBroadcastClientTrap("CellBroadcast", "CellBroadcast", System.getProperty("user.home") + File.separator + "config/cellbroadcast" + File.separator + "cellbroadcast.cfg");
        ipcClient.start();

        q = new SynchronousQueue<BroadcastDTO>();
        broadcastUnits = new BroadcastUnit[numberBroadCastUnit];
        for (int i = 0; i < broadcastUnits.length; i++) {
            broadcastUnits[i] = new BroadcastUnit("BroadcastUnit" + i, q, sched);
            broadcastUnits[i].execute();
        }

        broadcastThread = new BroadcastThread(q);
        ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder();
        threadFactoryBuilder.setNameFormat("BroadcastThread %d");

        Calendar now = Calendar.getInstance();
        int initialDelay = 60 - now.get(Calendar.SECOND) + 30;
        Executors.newSingleThreadScheduledExecutor(threadFactoryBuilder.build()).scheduleAtFixedRate(broadcastThread, initialDelay, CellBroadcastService.cycleGetBroadcast * 60, TimeUnit.SECONDS);

        monitorThread = new MonitorThread("MonitorThread", ipcName);
        monitorThread.execute();

    }

    public static void reLoadConfig() {
        ConfigurationManager.reloadConfiguration();
        numberBroadCastUnit = ConfigurationManager.getIntProp("cell_broadcast", "broadcast.unit.number", 2);
        maxTime = ConfigurationManager.getIntProp("cell_broadcast", "maxtime_s", 120);
        minTime = ConfigurationManager.getIntProp("cell_broadcast", "mintime_s", 120);
        timeout = ConfigurationManager.getIntProp("cell_broadcast", "timeout_s", 3) * 1000;
        interval = ConfigurationManager.getIntProp("cell_broadcast", "interval_s", 5) * 1000;
        cycleGetBroadcast = ConfigurationManager.getIntProp("cell_broadcast", "cell_broadcast.cycle_m", 1);
        CellBroadcastDAO.SQL_GET_LIST_BROADCAST = ConfigurationManager.getStrProp("cell_broadcast", "SQL_LIST_BROADCAST", "SELECT a.broadcasttime_id, a.zone_id, a.createdby, a.createddate, d.program_id, b.topic_id, c.zonename, a.starttime, a.program_indexs, a.cp_topic_id FROM broadcasttime a, cp_topic b, zones c, programs d where a.zone_id=c.zone_id and a.status=0 and d.status=1 and a.cp_topic_id=b.cp_topic_id and (a.cp_topic_id=d.cps_topic_id and a.program_indexs=d.indexs) and to_date(TO_CHAR(SYSDATE,'dd/MM/yyyy')||' '||a.starttime,'dd/MM/yyyy hh24:mi') between SYSDATE and SYSDATE+(10/1440)");
    }

    private void setupDatabaseAccess() {
        ConfigurationManager.reloadConfiguration();
        Properties properties = new Properties();
        properties.setProperty("url", ConfigurationManager.getStrProp("cell_broadcast", "URL", "jdbc:oracle:thin:@192.168.6.249:1521:mca"));
        properties.setProperty("classDriverName", ConfigurationManager.getStrProp("cell_broadcast", "DRIVER_CLASS_NAME", "oracle.jdbc.driver.OracleDriver"));
        properties.setProperty("user", ConfigurationManager.getStrProp("cell_broadcast", "USER_NAME", "livescreen"));
        properties.setProperty("password", ConfigurationManager.getStrProp("cell_broadcast", "PASSWORD", "livescreen"));

        DbAccess.getInstance().setProperties(properties);
        String enableMonitor = ConfigurationManager.getStrProp("cell_broadcast", "enableMonitor", "true");
        if (enableMonitor.equalsIgnoreCase("true")) {
            logger.debug("DB statistic is enabled, so scheduling it");
            long delay = ConfigurationManager.getLongProp("cell_broadcast", "monitorDelayMs", 60000L);
            ThreadFactory threadFactory = new BaseThreadFactory("DB statistic", "DB statistic");
            ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, threadFactory);
            DbAccess.getInstance().enableMonitor(executor, delay, delay);
        }
        ConnectionProvider connectionProvider = new ConnectionProvider() {

            @Override
            public Connection aquireConnection() {
                try {
                    return DbAccess.getInstance().getConnection();
                } catch (SQLException ex) {
                    logger.error("", ex);
                    return null;
                }
            }

            @Override
            public void releaseConnection(Connection connection) {
                DbAccess.closeConnection(connection);
            }
        };
        ConnectionProviderManager.getInstance().setConnectionProvider(connectionProvider);
        logger.info("Done setup Database access");
    }

    private void scheduleRebuildAllZoneTimeline() {
        ConfigurationManager.reloadConfiguration();
        String[] timelineRebuildTime = ConfigurationManager.getStrProp("cell_broadcast", "timelineRebuildTime", "06:00").split(":");
        Integer hour = Integer.parseInt(timelineRebuildTime[0]);
        Integer minute = Integer.parseInt(timelineRebuildTime[1]);

        DateTime now = DateTime.now();
        DateTime firstRunTime = now.withTime(hour, minute, 0, 0);
        if (firstRunTime.isBefore(now)) {
            firstRunTime = firstRunTime.plusDays(1);
        }
        int initialDelay = Minutes.minutesBetween(now, firstRunTime).getMinutes();
        int period = ConfigurationManager.getIntProp("cell_broadcast", "timelineRebuildPeriodInMinutes", 24 * 60);
        ScheduleRebuild timelineRebuild = new ScheduleRebuild();
        ThreadFactory threadFactory = new BaseThreadFactory("ScheduleRebuild", "ScheduleRebuild");
        Executors.newSingleThreadScheduledExecutor(threadFactory).scheduleAtFixedRate(timelineRebuild, initialDelay, period, TimeUnit.MINUTES);

        ScheduleRebuildCommandChecker commandChecker = new ScheduleRebuildCommandChecker();
        Executors.newSingleThreadScheduledExecutor(threadFactory).scheduleAtFixedRate(commandChecker, 1, 15, TimeUnit.SECONDS);
    }

    private void scheduleTimeslotBuilder() {
        ConfigurationManager.reloadConfiguration();
        DateTime now = DateTime.now();
        int initialDelay = (60 - now.getSecondOfMinute()) + 1;
        int pickAheadMinutes = ConfigurationManager.getIntProp("cell_broadcast", "timeSlotBuilderPickAheadMinutes", 2);
        ZoneTimeslotBuilder zoneTimeslotBuilder = new ZoneTimeslotBuilder(pickAheadMinutes);
        ThreadFactory threadFactory = new BaseThreadFactory("TimeSlotBuilder", "TimeSlotBuilder");
        Executors.newSingleThreadScheduledExecutor(threadFactory).scheduleAtFixedRate(zoneTimeslotBuilder, initialDelay, 60, TimeUnit.SECONDS);
    }

    /**
     * @param args
     * @throws Throwable
     */
    public static void main(String[] args) throws Throwable {
        if ("sb".equals(args[0])) {
            SmsBroadcastMain smsBroadcastMain = new SmsBroadcastMain();
            smsBroadcastMain.init(args);
        } else {
            CellBroadcastService cellBroadcastService = new CellBroadcastService(args[0]);
        }
    }

    private static String getMostContent(String addresses[]) {
        int howMany;
        int mostTimes = 1;
        String mostCommon = null;
        for (int i = 0; i < addresses.length; i++) {
            if (addresses[i] == null || addresses[i].length() == 0) {
                continue;
            }
            howMany = 0;
            for (int j = 0; j < addresses.length; j++) {
                if (addresses[i].equals(addresses[j])) {
                    howMany++;
                }
            }

            if (howMany > mostTimes) {
                mostTimes = howMany;
                mostCommon = addresses[i];
            }
        }

        return mostCommon;
    }

    private static int getMostNumber(int addresses[]) {
        int howMany;
        int mostTimes = 1;
        int mostCommon = 0;
        for (int i = 0; i < addresses.length; i++) {
            if (addresses[i] == 0) {
                continue;
            }
            howMany = 0;
            for (int j = 0; j < addresses.length; j++) {
                if (addresses[i] == (addresses[j])) {
                    howMany++;
                }
            }

            if (howMany > mostTimes) {
                mostTimes = howMany;
                mostCommon = addresses[i];
            }
        }

        return mostCommon;
    }

    public static CBM_SUBMIT_RESP generateCBMStructFile(MenuItemDTO[] menu, String detail, int messageID, BroadcastDTO broadcastDTO)
            throws IOException {
        int detailSize = detail.length();
        String beginHex = "";
        String menuHex = "";
        String detailHex = "";
        String separators = ConfigurationManager.getStrProp("cell_broadcast", "separators", "");

        ByteBuffer b = ByteBuffer.allocate(4);
        b.putInt(messageID);
        byte[] array = Arrays.copyOfRange(b.array(), 3, 4);//chi lay byte cuoi
        String messageIdHex1Byte = Hex.convertBinary2Hexadecimal(array);

        int[] number = new int[menu.length];
        for (int i = 0; i < number.length; i++) {
            number[i] = menu[i].getShortcode();
        }
        int defaultNumber = getMostNumber(number);

        String[] content = new String[menu.length];
        for (int i = 0; i < content.length; i++) {
            content[i] = menu[i].getContent();
            logger.debug("content{}: {}", i, content[i]);
        }
        String defaultContent = getMostContent(content);

        //for Test
//		defaultNumber = 0;
//		defaultContent = null;
        int topicID = 0;//fix for mobifone
        logger.debug("[MsgId: {}]Start broadcast for topicID: {}---defaultNumber={}---defaultContent={}", messageIdHex1Byte, topicID, defaultNumber, defaultContent);

        if (defaultNumber == 0 && defaultContent == null) {//OK DONE
            //<editor-fold defaultstate="collapsed" desc="No shortcode, no content">
            for (int i = 0; i < menu.length; i++) {
                if (menu[i].getType().startsWith("ZZ")) {
                    menuHex += menu[i].getType();
                    menuHex += Hex.integerToHex(menu[i].getContent().length());
                } else {
                    menuHex += menu[i].getType() + "81";
                    menuHex += toShortCodeString(menu[i].getShortcode());

                    String tmp = Hex.integerToHex(menu[i].getContent().length());
                    if (menu[i].getType().equals("B3")) {
                        tmp = tmp.replaceFirst("0", "8");
                    }

                    menuHex += tmp;
                }

                menuHex += Hex.stringToHex(menu[i].getContent());
                menuHex += Hex.integerToHex(menu[i].getDisplay().length()) + Hex.stringToHex(menu[i].getDisplay());
            }

            // Init
            int numberMenu = menu.length;
            beginHex = ACTION_CODE
                    + "0" + numberMenu + Hex.integerToHex(topicID)
                    + "0000" + separators;
            int beginLength = (beginHex.length() + menuHex.length()) / 2;
            beginHex = Hex.integerToHex(beginLength) + beginHex;

            detailHex = "010200" + Hex.integerToHex(detailSize + 2) + Hex.stringToHex(detail);
            //</editor-fold>
        } else if (defaultNumber != 0 && defaultContent == null) {//OK DONE
            //<editor-fold defaultstate="collapsed" desc="Have shortcode, No content">
            for (int i = 0; i < menu.length; i++) {
                if (menu[i].getType().equals("A3")) {
                    if (menu[i].getShortcode() == defaultNumber) {
                        menu[i].setType("A0");
                        menuHex += menu[i].getType();
                    } else {
                        menuHex += menu[i].getType() + "81";
                        menuHex += toShortCodeString(menu[i].getShortcode());
                    }
                    menuHex += Hex.integerToHex(menu[i].getContent().length());

                } else {
                    if (menu[i].getShortcode() == defaultNumber) {
                        menu[i].setType("B0");
                        menuHex += menu[i].getType();
                    } else {
                        menuHex += menu[i].getType() + "81";
                        menuHex += toShortCodeString(menu[i].getShortcode());
                    }

                    String tmp = Hex.integerToHex(menu[i].getContent().length());
                    if (menu[i].getType().equals("B3")) {
                        tmp = tmp.replaceFirst("0", "8");
                    }
                    menuHex += tmp;
                }

                menuHex += Hex.stringToHex(menu[i].getContent());
                menuHex += Hex.integerToHex(menu[i].getDisplay().length()) + Hex.stringToHex(menu[i].getDisplay());
            }

            // Init
            int numberMenu = menu.length;
            beginHex = ACTION_CODE
                    + "0" + numberMenu + Hex.integerToHex(topicID)
                    + "0381" + toShortCodeString(defaultNumber) + "00" + separators;
            int beginLength = (beginHex.length() + menuHex.length()) / 2;
            beginHex = Hex.integerToHex(beginLength) + beginHex;

            detailHex = "010200" + Hex.integerToHex(detailSize + 2) + Hex.stringToHex(detail);

            //</editor-fold>
        } else if (defaultNumber == 0 && defaultContent != null) {
            //<editor-fold defaultstate="collapsed" desc="No shortcode, have content">
            for (int i = 0; i < menu.length; i++) {
                menuHex += menu[i].getType() + "81";
                menuHex += toShortCodeString(menu[i].getShortcode());

                if (menu[i].getContent().equals(defaultContent)) {
                    if (menu[i].getType().startsWith("A")) {
                        menuHex += "00";
                    } else {
                        menuHex += "80";
                    }
                } else {
                    if (menu[i].getType().startsWith("A")) {
                        menuHex += Hex.integerToHex(menu[i].getContent().length());
                    } else {
                        String tmp = Hex.integerToHex(menu[i].getContent().length());
                        if (menu[i].getType().equals("B3")) {
                            tmp = tmp.replaceFirst("0", "8");
                        }
                        menuHex += tmp;
                    }

                    menuHex += Hex.stringToHex(menu[i].getContent());
                }

                menuHex += Hex.integerToHex(menu[i].getDisplay().length()) + Hex.stringToHex(menu[i].getDisplay());
                //
                //
                //				menuHex += Hex.stringToHex(menu[i].getContent());
                //				menuHex += Hex.IntegerToHex(menu[i].getDisplay().length()) + Hex.stringToHex(menu[i].getDisplay());
                //
                //				menuSize += menu[i].getContent().length() + 1;
                //				menuSize += menu[i].getDisplay().length() + 1;
            }
            //ThanhTT- Them 23-10-2012
            defaultContent = Hex.stringToHex(defaultContent);
            //End- ThanhTT

            // Init
            int numberMenu = menu.length;
            beginHex = ACTION_CODE
                    + "0" + numberMenu + Hex.integerToHex(topicID)
                    + "00" + Hex.integerToHex(defaultContent.length() / 2) + defaultContent + separators;
            int beginLength = (beginHex.length() + menuHex.length()) / 2;
            beginHex = Hex.integerToHex(beginLength) + beginHex;

            detailHex = "010200" + Hex.integerToHex(detailSize + 2) + Hex.stringToHex(detail);

            //</editor-fold>
        } else if (defaultNumber != 0 && defaultContent != null) {
            //<editor-fold defaultstate="collapsed" desc="Have shortcode, have content">
            for (int i = 0; i < menu.length; i++) {
                if (menu[i].getType().equals("A3")) {
                    if (menu[i].getShortcode() == defaultNumber) {
                        menu[i].setType("A0");
                        menuHex += menu[i].getType();
                    } else {
                        menuHex += menu[i].getType() + "81";
                        menuHex += toShortCodeString(menu[i].getShortcode());
                    }
                } else {
                    if (menu[i].getShortcode() == defaultNumber) {
                        menu[i].setType("B0");
                        menuHex += menu[i].getType();
                    } else {
                        menuHex += menu[i].getType() + "81";
                        menuHex += toShortCodeString(menu[i].getShortcode());
                    }
                }

                if (menu[i].getContent().equals(defaultContent)) {
                    if (menu[i].getType().startsWith("A")) {
                        menuHex += "00";
                    } else {
                        menuHex += "80";
                    }
                } else {
                    if (menu[i].getType().startsWith("A")) {
                        menuHex += Hex.integerToHex(menu[i].getContent().length());
                    } else {
                        String tmp = Hex.integerToHex(menu[i].getContent().length());
                        if (menu[i].getType().equals("B3")) {
                            tmp = tmp.replaceFirst("0", "8");
                        }
                        menuHex += tmp;
                    }

                    menuHex += Hex.stringToHex(menu[i].getContent());
                }

                menuHex += Hex.integerToHex(menu[i].getDisplay().length()) + Hex.stringToHex(menu[i].getDisplay());
            }
            //ThanhTT- Them 23-10-2012
            defaultContent = Hex.stringToHex(defaultContent);
            //End- ThanhTT

            // Init
            int numberMenu = menu.length;
            beginHex = ACTION_CODE
                    + "0" + numberMenu + Hex.integerToHex(topicID)
                    + "0381" + toShortCodeString(defaultNumber) + Hex.integerToHex(defaultContent.length() / 2) + defaultContent + separators;
            int beginLength = (beginHex.length() + menuHex.length()) / 2;
            beginHex = Hex.integerToHex(beginLength) + beginHex;

            detailHex = "010200" + Hex.integerToHex(detailSize + 2) + Hex.stringToHex(detail);

            //</editor-fold>
        }

        int currentSize = (beginHex.length() + menuHex.length() + detailHex.length()) / 2;
        logger.debug("[MsgId: {}]Current size: {}", messageIdHex1Byte, currentSize);
        int remaining = SIZE_TO_COPY - (currentSize % SIZE_TO_COPY);
        int numOfParts = (currentSize / SIZE_TO_COPY) + 1;
        logger.debug("[MsgId: {}]remaining size: {}", messageIdHex1Byte, remaining);
        for (int i = 0; i < remaining; i++) {
            detailHex += "0D";
        }
        logger.debug("[MsgId: {}]Test broadcast: {}---{}---{}", messageIdHex1Byte, beginHex, menuHex, detailHex);

        byte[] beginArray = Hex.hexStringToByteArray(beginHex);
        byte[] menuArray = Hex.hexStringToByteArray(menuHex);
        byte[] detailArray = Hex.hexStringToByteArray(detailHex);

        byte[] sum = new byte[beginArray.length + menuArray.length + detailArray.length];
        System.arraycopy(beginArray, 0, sum, 0, beginArray.length);
        System.arraycopy(menuArray, 0, sum, beginArray.length, menuArray.length);
        System.arraycopy(detailArray, 0, sum, beginArray.length + menuArray.length, detailArray.length);

        if (SmsBroadcastConfig.smsBroadcastEnable()) {
            ConfigurationManager.reloadConfiguration();
            String[] mdnList = ConfigurationManager.getStrProp("cell_broadcast", "SmsBroadcast.global.msisdns", "").split(",");
//            byte smsBroadcastId = TestSmsAsCB2.getSmsBroadcastId(messageID);
            SmsBroadcaster smsBroadcaster = new SmsBroadcaster(sum, messageID, mdnList);
            new Thread(smsBroadcaster).start();
        }

        String startTime = broadcastDTO.getStartTime();
        CellBroadcastDAO dao = new CellBroadcastDAO();
        int distanceTime = dao.calculteTimeDistance(startTime);
        if (distanceTime > CellBroadcastService.maxTime) {
            distanceTime = CellBroadcastService.maxTime;
        }

        if (distanceTime < CellBroadcastService.minTime) {
            distanceTime = CellBroadcastService.minTime;
        }
        logger.debug("[MsgId: {}]Test distanceTime: {}---{}---{}", messageIdHex1Byte, distanceTime, numOfParts, interval / 1000);

        int numberLoop;
        if (ConfigurationManager.getBooleanProp("cell_broadcast", "fixed_numberLoop", false)) {
            //gianglt: bo cách lap cu, so lan lap se dc config
            //tham so config là: numberLoop (giá tri mac dinh = 1)
            numberLoop = ConfigurationManager.getIntProp("cell_broadcast", "numberLoop", 1);
            logger.debug("[MsgId: {}]Number of loops is fixed as {}", messageIdHex1Byte, numberLoop);
        } else {
            numberLoop = (int) (distanceTime / (numOfParts * ((int) (CellBroadcastService.interval / 1000))));
//            numberLoop = numberLoop - 1;//Giam di 1 lan de tranh goi len nhau
            logger.debug("[MsgId: {}]Number of loops is {}", messageIdHex1Byte, numberLoop);
        }

        CBM_SUBMIT_RESP resp = new CBM_SUBMIT_RESP();
        resp.setErrorCode(CBM_SUBMIT_RESP.CBM_SUMBIT_RESULT.UNKNOWN.value());

        while (numberLoop > 0) {
            numberLoop--;
            int index = 0;
            Random rnd = new Random();
            int newIndex = rnd.nextInt(10000);
            for (int curPartIdx = 0; curPartIdx < numOfParts; curPartIdx++) {
                byte[] out_file = new byte[82];
                String start = messageIdHex1Byte + "" + (curPartIdx + 1) + "" + numOfParts;
                byte[] startArray = Hex.hexStringToByteArray(start);
                System.arraycopy(startArray, 0, out_file, 0, startArray.length);

                logger.debug("[MsgId: {}]COPY: sumArray[{}]: {}", messageIdHex1Byte, sum.length, Hex.convertBinary2Hexadecimal(sum));
                System.arraycopy(sum, index, out_file, startArray.length, SIZE_TO_COPY);
                index += SIZE_TO_COPY;
                logger.debug("[MsgId: {}]COPY: next index: {}", messageIdHex1Byte, index);
                out_file[81] = (byte) 0x00; //Vina = FF, Mobi = 00, Viettel = 07
                logger.debug("[MsgId: {}]COPY: out_fileArray[{}]: {}", messageIdHex1Byte, out_file.length, Hex.convertBinary2Hexadecimal(out_file));

                // Write to file
                String path = System.getProperty("user.home") + File.separator + "cbm";
                File f = new File(path);
                if (!f.exists()) {
                    f.mkdir();
                }

                FileOutputStream op = new FileOutputStream(new File(path + File.separator + "news" + newIndex + "_CBM" + curPartIdx));
                op.write(out_file);
                op.flush();
                op.close();

                CBM_SUBMIT_REQ req = new CBM_SUBMIT_REQ();
                req.setSeqId(GeneratorSeqIPC.getNextSeq());
                req.setZoneId((long) broadcastDTO.getZoneID());
                req.setZoneName(broadcastDTO.getZone());
                req.setData(Hex.convertBinary2Hexadecimal(out_file));
                req.setScheduleId(broadcastDTO.getScheduleId());
                req.setCbmId(UUID.randomUUID().toString());
                req.setTotalParts(numOfParts);
                req.setCurrentPart(curPartIdx + 1);
                if (curPartIdx + 1 == numOfParts) {
                    req.setIsLastPart(Boolean.TRUE);
                } else {
                    req.setIsLastPart(Boolean.FALSE);
                }

                try {
                    logger.info("[MsgId: {}][Zone {}] Sending IPC request: {}", messageIdHex1Byte, req.getZoneName(), req);
                    resp = (CBM_SUBMIT_RESP) ipcClient.syncRequest(req, CellBroadcastService.timeout);
                    logger.info("[MsgId: {}][Zone {}] Received IPC response: {}", messageIdHex1Byte, req.getZoneName(), resp);
                } catch (ResponseTimeoutException e1) {
                    logger.error("[MsgId: {}]ResponseTimeoutException: ", messageIdHex1Byte, e1);
                } catch (InvalidResponseException e1) {
                    logger.error("[MsgId: {}]InvalidResponseException: ", messageIdHex1Byte, e1);
                } catch (Throwable e1) {
                    logger.error("[MsgId: {}]Exception: ", messageIdHex1Byte, e1);
                }
//                LOG.info("Broadcast result: " + resp.getErrorCode() + " - " + resp.getErrorDetail() + " - CBC TransactionId: " + resp.getCbcTransactionId());
//                dao.insertCbcTransactionMap(broadcastId, resp.getCbcTransactionId());
                if (curPartIdx < numOfParts - 1) {
                    try {
                        Thread.sleep(CellBroadcastService.interval);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }

        return resp;
    }

    private static String toShortCodeString(int defaultCode) {
        StringBuilder shortCode = new StringBuilder(String.valueOf(defaultCode));
        if (shortCode.length() % 2 == 1) {
            shortCode.append("F");
        }

        StringBuilder result = new StringBuilder();
        for (int i = 0; i < shortCode.length(); i += 2) {
            result.append(shortCode.charAt(i + 1)).append(shortCode.charAt(i));
        }

        return result.toString();
    }

    private static void arraycopy(byte[] srcArray, int srcStartIdx, byte[] dstArray, int dstStartIdx, int copyLength) {
        int dstIdx = dstStartIdx;
        for (int srcIdx = srcStartIdx; srcIdx < srcStartIdx + copyLength; srcIdx++) {
            logger.debug("Copy from src[{}]: {} to dst[{}]", srcIdx, dstArray[dstIdx], dstIdx);
            dstArray[dstIdx++] = srcArray[srcIdx];
        }

    }
}
